﻿
 // Подсчет когнитивной сложности методов (регистрируется как ошибка только сложность > 100).
 // Не учитываются косвенные рекурсивные вызовы.

 // Метод очень спорный. Никакого отношения к реальной когнитивной нагрузке скорее всего не имеет.

 
Перем Типы;
Перем Токены;
Перем ТаблицаОшибок;
Перем ТаблицаТокенов;
Перем Результат;

Перем Уровень;
Перем КогнитивнаяСложность;
Перем ТекущийМетод;
Перем УровеньВыражения;

Процедура Открыть(Парсер, Параметры) Экспорт
	Типы = Парсер.Типы();
	Токены = Парсер.Токены();
	ТаблицаОшибок = Парсер.ТаблицаОшибок();
	ТаблицаТокенов = Парсер.ТаблицаТокенов();
	Результат = Новый Массив;
	Уровень = 1;
	КогнитивнаяСложность = 0;
	УровеньВыражения = 0;
КонецПроцедуры // Открыть()

Функция Подписки() Экспорт
	Перем Подписки;
	Подписки = Новый Массив;
	Подписки.Добавить("ПосетитьОбъявлениеМетода");
	Подписки.Добавить("ПокинутьОбъявлениеМетода");
	Подписки.Добавить("ПосетитьВыражениеБинарное");
	Подписки.Добавить("ПокинутьВыражениеБинарное");
	Подписки.Добавить("ПосетитьВыражениеТернарное");
	Подписки.Добавить("ПосетитьОператорЕсли");
	Подписки.Добавить("ПокинутьОператорЕсли");
	Подписки.Добавить("ПосетитьОператорИначеЕсли");
	Подписки.Добавить("ПосетитьОператорИначе");
	Подписки.Добавить("ПосетитьОператорПока");
	Подписки.Добавить("ПокинутьОператорПока");
	Подписки.Добавить("ПосетитьОператорДля");
	Подписки.Добавить("ПокинутьОператорДля");
	Подписки.Добавить("ПосетитьОператорДляКаждого");
	Подписки.Добавить("ПокинутьОператорДляКаждого");
	Подписки.Добавить("ПосетитьОператорИсключение");
	Подписки.Добавить("ПосетитьОператорВызоваПроцедуры");
	Подписки.Добавить("ПосетитьОператорПерейти");
	Подписки.Добавить("ПосетитьОператорПрервать");
	Подписки.Добавить("ПосетитьОператорПродолжить");
	Возврат Подписки;
КонецФункции // Подписки()

Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
	ТекущийМетод = ОбъявлениеМетода.Сигнатура;
КонецПроцедуры // ПосетитьОбъявлениеМетода()

Процедура ПокинутьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
	Если КогнитивнаяСложность > 100 Тогда
		Текст = СтрШаблон("Когнитивная сложность %1", КогнитивнаяСложность);
		Ошибка(Текст, ОбъявлениеМетода.Начало, ОбъявлениеМетода.Конец);
	КонецЕсли;
	Уровень = 1;
	КогнитивнаяСложность = 0;
КонецПроцедуры // ПокинутьОбъявлениеМетода()

Процедура ПосетитьВыражениеБинарное(ВыражениеБинарное) Экспорт
	Если УровеньВыражения = 0 Тогда  // только для корневого
		СписокОпераций = Новый Массив;
		ПостроитьСписокОпераций(СписокОпераций, ВыражениеБинарное);
		ТекущаяОперация = Неопределено;
		Для Каждого Операция Из СписокОпераций Цикл
			Если Операция <> ТекущаяОперация Тогда
				ТекущаяОперация = Операция;
				Если ТекущаяОперация = Токены.Или
					Или ТекущаяОперация = Токены.И Тогда
					КогнитивнаяСложность = КогнитивнаяСложность + 1;
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	УровеньВыражения = УровеньВыражения + 1;
КонецПроцедуры // ПосетитьВыражениеБинарное()

Процедура ПокинутьВыражениеБинарное(ВыражениеБинарное) Экспорт
	УровеньВыражения = УровеньВыражения - 1;
КонецПроцедуры // ПокинутьВыражениеБинарное()

Процедура ПостроитьСписокОпераций(СписокОпераций, ВыражениеБинарное)
	Если ВыражениеБинарное.ЛевыйОперанд.Тип = Типы.ВыражениеБинарное Тогда
		ПостроитьСписокОпераций(СписокОпераций, ВыражениеБинарное.ЛевыйОперанд);
	КонецЕсли;
	СписокОпераций.Добавить(ВыражениеБинарное.Операция.Токен);
	Если ВыражениеБинарное.ПравыйОперанд.Тип = Типы.ВыражениеБинарное Тогда
		ПостроитьСписокОпераций(СписокОпераций, ВыражениеБинарное.ПравыйОперанд);
	КонецЕсли;
КонецПроцедуры // ПостроитьСписокОпераций()

Процедура ПосетитьВыражениеТернарное(ВыражениеТернарное) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + Уровень;
КонецПроцедуры // ПосетитьВыражениеТернарное()

Процедура ПосетитьОператорВызоваПроцедуры(ОператорВызоваПроцедуры) Экспорт
	Если ОператорВызоваПроцедуры.Идентификатор.Голова.Объявление = ТекущийМетод Тогда
		КогнитивнаяСложность = КогнитивнаяСложность + 1;
	КонецЕсли;
КонецПроцедуры // ПосетитьОператорВызоваПроцедуры()

Процедура ПосетитьОператорЕсли(ОператорЕсли) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + Уровень;
	Уровень = Уровень + 1;
КонецПроцедуры // ПосетитьОператорЕсли()

Процедура ПокинутьОператорЕсли(ОператорЕсли) Экспорт
	Уровень = Уровень - 1;
КонецПроцедуры // ПокинутьОператорЕсли()

Процедура ПосетитьОператорИначеЕсли(ОператорИначеЕсли) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + 1;
КонецПроцедуры // ПосетитьОператорИначеЕсли()

Процедура ПосетитьОператорИначе(ОператорИначе) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + 1;
КонецПроцедуры // ПосетитьОператорИначе()

Процедура ПосетитьОператорПока(ОператорПока) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + Уровень;
	Уровень = Уровень + 1;
КонецПроцедуры // ПосетитьОператорПока()

Процедура ПокинутьОператорПока(ОператорПока) Экспорт
	Уровень = Уровень - 1;
КонецПроцедуры // ПокинутьОператорПока()

Процедура ПосетитьОператорДля(ОператорДля) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + Уровень;
	Уровень = Уровень + 1;
КонецПроцедуры // ПосетитьОператорДля()

Процедура ПокинутьОператорДля(ОператорДля) Экспорт
	Уровень = Уровень - 1;
КонецПроцедуры // ПокинутьОператорДля()

Процедура ПосетитьОператорДляКаждого(ОператорДляКаждого) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + Уровень;
	Уровень = Уровень + 1;
КонецПроцедуры // ПосетитьОператорДляКаждого()

Процедура ПокинутьОператорДляКаждого(ОператорДляКаждого) Экспорт
	Уровень = Уровень - 1;
КонецПроцедуры // ПокинутьОператорДляКаждого()

Процедура ПосетитьОператорИсключение(ОператорИсключение) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + Уровень;
	Уровень = Уровень + 1;
КонецПроцедуры // ПосетитьОператорИсключение()

Процедура ПосетитьОператорПерейти(ОператорПерейти) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + 1;
КонецПроцедуры // ПосетитьОператорПерейти()

Процедура ПосетитьОператорПрервать(ОператорПрервать) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + 1;
КонецПроцедуры // ПосетитьОператорПрервать()

Процедура ПосетитьОператорПродолжить(ОператорПродолжить) Экспорт
	КогнитивнаяСложность = КогнитивнаяСложность + 1;
КонецПроцедуры // ПосетитьОператорПродолжить()

Функция Закрыть() Экспорт
	Возврат СтрСоединить(Результат, Символы.ПС);
КонецФункции

Процедура Ошибка(Текст, Начало, Конец = Неопределено, ЕстьЗамена = Ложь)
	Ошибка = ТаблицаОшибок.Добавить();
	Ошибка.Источник = "ПодсчетКогнитивнойСложностиМетодов";
	Ошибка.Текст = Текст;
	Ошибка.ПозицияНачала = Начало.Позиция;
	Ошибка.НомерСтрокиНачала = Начало.НомерСтроки;
	Ошибка.НомерКолонкиНачала = Начало.НомерКолонки;
	Если Конец = Неопределено Или Конец = Начало Тогда
		Ошибка.ПозицияКонца = Начало.Позиция + Начало.Длина;
		Ошибка.НомерСтрокиКонца = Начало.НомерСтроки;
		Ошибка.НомерКолонкиКонца = Начало.НомерКолонки + Начало.Длина;
	Иначе
		Ошибка.ПозицияКонца = Конец.Позиция + Конец.Длина;
		Ошибка.НомерСтрокиКонца = Конец.НомерСтроки;
		Ошибка.НомерКолонкиКонца = Конец.НомерКолонки + Конец.Длина;
	КонецЕсли;
	Ошибка.ЕстьЗамена = ЕстьЗамена;
КонецПроцедуры